package com.jlboot.core.util;

import com.alibaba.fastjson.JSON;
import com.jfinal.kit.StrKit;
import com.jlboot.core.ConfigUtil;
import com.jlboot.core.consts.CoreResponseCode;
import com.jlboot.core.exception.JlLogicException;
import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by 51594 on 2022/7/27.
 */
public class JwtUtil {

    /**
     *
     */
    public static long EXP_DATE = 24 * 60 * 60 * 1000;
    /**
     *
     */
    protected static boolean DEBUG = ConfigUtil.DEBUG();

    /**
     * 签发JWT
     *
     * @param id
     * @param subject   可以是JSON数据 尽可能少
     * @param ttlMillis
     * @return String
     */
    public static String createJWT(String id, JwtSubject subject, long ttlMillis) {
        return createJWT(id, subject.toJson(), ttlMillis);
    }


//    /**
//     * 签发JWT
//     *
//     * @param uid
//     * @return String
//     */
//    public static String createJWT(int uid) {
//        JwtUtil.JwtSubject js = new JwtUtil.JwtSubject();
//        js.setUserid(uid);
//        return createJWT("jwt", js, EXP_DATE);
//    }

    /**
     * 签发JWT
     *
     * @param id
     * @param subject   可以是JSON数据 尽可能少
     * @param ttlMillis
     * @return String
     */
    public static String createJWT(String id, String subject, long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject)   // 主题
                .setIssuer("user")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            builder.setExpiration(expDate); // 过期时间
        }
        return builder.compact();
    }

    /**
     * 信息
     *
     * @param jwtStr
     * @return
     */
    public static JwtSubject getJwtInfo(String jwt) {

        if(StrKit.isBlank(jwt)){
            throw new JlLogicException("你未登陆");
        }

        try {
            Claims claims = parseJWT(jwt);
            //检查是否已经注销
            if(JwtPool.checkJwt(jwt)){
                throw new JlLogicException(CoreResponseCode.TOKEN_INVALID.getCode(),CoreResponseCode.TOKEN_INVALID.getName());
            }
            return JSON.parseObject(claims.getSubject(), JwtSubject.class);
        } catch (ExpiredJwtException e) {
            throw new JlLogicException(CoreResponseCode.LOGIN_EXPIRED.getCode(),CoreResponseCode.LOGIN_EXPIRED.getName());
        } catch (SignatureException e) {
            throw new JlLogicException("无效签名");
        } catch (Exception e) {
            throw new JlLogicException("你未登陆");
        }
    }

    /**
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = ConfigUtil.getProperty("JWT_SECERT", "DEFAULT_KEY_9423156").getBytes();
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 解析JWT字符串
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    private static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }

    public static class JwtSubject {

        public String id;//会话ID
        public Long userId = 0L;
        public int pid = 0;
        public Long deptId = 0L;
        public Long orgId = 0L;
        public Long tempId = 0L;
        public String username;
        public String roles;
        public String posts;
        public long lastLogin = 0;

        public JwtSubject() {
        }

        public JwtSubject(String id) {
            this.id = id;
        }


        public JwtSubject setId(String id) {
            this.id = id;
            return this;
        }

        public JwtSubject(Long userid, String username, String roles) {
            this.userId = userid;
            this.username = username;
            this.roles = roles;
        }

        public JwtSubject(Long userid, Long deptid, String username, String roles) {
            this.userId = userid;
            this.deptId = deptid;
            this.username = username;
            this.roles = roles;
        }


        public JwtSubject(Long userid, Long deptid, String username, String roles, long lastlogin) {
            this.userId = userid;
            this.deptId = deptid;
            this.username = username;
            this.roles = roles;
            this.lastLogin = lastlogin;
        }

        public String getId() {
            return id;
        }

        public Long getUserId() {
            return userId;
        }

        public void setUserId(Long userId) {
            this.userId = userId;
        }

        public int getPid() {
            return pid;
        }

        public void setPid(int pid) {
            this.pid = pid;
        }

        public Long getDeptId() {
            return deptId;
        }

        public void setDeptId(Long deptId) {
            this.deptId = deptId;
        }

        public Long getOrgId() {
            return orgId;
        }

        public void setOrgId(Long orgId) {
            this.orgId = orgId;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getRoles() {
            return roles;
        }

        public void setRoles(String roles) {
            this.roles = roles;
        }

        public String getPosts() {
            return posts;
        }

        public void setPosts(String posts) {
            this.posts = posts;
        }

        public long getLastLogin() {
            return lastLogin;
        }

        public void setLastLogin(long lastLogin) {
            this.lastLogin = lastLogin;
        }

        public String toJson() {
            return JSON.toJSONString(this);
        }
    }


    /**
     * //TODO 长时间运行会出现大量无效数据，可以定时或者某种情况下，清理下已过期的数据
     * jwt黑名单
     */
    public static class JwtPool {
        private static List<String> JWT_LIST = new ArrayList<>();

        public static void addJwt(String jwt) {
            JWT_LIST.add(jwt);
        }

        /**
         * @param jwt
         * @return
         */
        public static boolean checkJwt(String jwt) {
            for (int k = 0; k < JWT_LIST.size(); k++) {
                String jwts = String.valueOf(JWT_LIST.get(k));
                if (jwt.equals(jwts)) {
                    return true;
                }
            }
            return false;
        }
    }
}
